package edu.pku.sei.transformation.atl.adapter;

import java.util.ArrayList;
import java.util.List;

import edu.pku.sei.transformation.atl.rule.EATLRuleType;
import edu.pku.sei.transformation.atl.rule.IATLExpression;
import edu.pku.sei.transformation.atl.rule.IATLLink;
import edu.pku.sei.transformation.atl.rule.IATLPattern;
import edu.pku.sei.transformation.atl.rule.IATLRule;
import edu.pku.sei.transformation.atl.rule.IATLVariable;
import edu.pku.sei.transformation.editor.model.Expression;
import edu.pku.sei.transformation.editor.model.GraphicalDomain;
import edu.pku.sei.transformation.editor.model.Rule;
import edu.pku.sei.transformation.editor.model.RuleInvoke;
import edu.pku.sei.transformation.editor.model.TypedModel;
import edu.pku.sei.transformation.editor.model.variable.Variable;
import edu.pku.sei.transformation.editor.views.problem.ProblemView;

public class ATLRuleAdapter implements IATLRule {
	Rule rule = null;
	ATLPatternAdapter source;
	ATLPatternAdapter target;
	List<ATLInvokeExpression> invokes = new ArrayList<ATLInvokeExpression>();
	List<IATLExpression> imperativeExps = new ArrayList<IATLExpression>();
	List<IATLVariable> variables = new ArrayList<IATLVariable>();

	public ATLRuleAdapter(Rule rule, TypedModel direction) throws Exception {
		this.rule = rule;
		// adapt rule invoke
		for (Object obj : rule.getInvoke()) {
			if (obj instanceof RuleInvoke) {
				RuleInvoke invoke = (RuleInvoke) obj;
				if (invoke.getInvokeType() == invoke.RULE_POSTCONDITION) {
					try {
						ATLInvokeExpression invokeExp = new ATLPostRuleInvokeExpression(
								invoke, direction);
						invokes.add(invokeExp);
					} catch (Exception e) {
						e.printStackTrace();
					}

				} else if (invoke.getInvokeType() == invoke.RULE_PRECONDITION) {
					try {
						ATLInvokeExpression invokeExpression = new ATLPreRuleInvokeExpression(
								invoke, direction);
					} catch (Exception e) {
						e.printStackTrace();
					}
				} else {
					ProblemView.addError(rule.getModel(),
							ProblemView.CODEGEN_ATL, "unknown invoke",
							"invoke with unknown type", rule.getName() + "->"
									+ invoke.getCallee().getName());
				}
			}
		}

		// in our tool, only support one target end
		for (Object obj : rule.getDomains()) {
			if (obj instanceof GraphicalDomain) {
				GraphicalDomain d = (GraphicalDomain) obj;

				if (d.getTypedModel() == direction)
					this.target = new ATLPatternAdapter(d.getPattern());
				else
					source = new ATLPatternAdapter(d.getPattern());
			}
		}
		// bind rule invoke to node
		// move node's constraint to rule
		for (ATLInvokeExpression invoke : invokes) {
			ATLNodeAdapter node = (ATLNodeAdapter) target
					.getNode(invoke.output);
			if (node == null) {
				ProblemView.addError(rule.getModel(), ProblemView.CODEGEN_ATL,
						"invoke target error", "invoke target:" + invoke.target
								+ " can not find, or is not a variable", rule
								.getName()
								+ "->"
								+ invoke.ruleInvoke.getCallee().getName());
				throw new Exception("invoke target not find in rule");
			}
			for (IATLExpression exp : node.getExps()) {
				ATLExpressionAdapter expAdapter = (ATLExpressionAdapter) exp;
				expAdapter.position = ExpressionPosition.rule;
				imperativeExps.add(expAdapter);
			}
			for (IATLLink link : node.getLinks()) {
				ATLLinkAdapter linkAdapter = (ATLLinkAdapter) link;
				imperativeExps.add(linkAdapter);
			}

		}
		// add body exp to rule
		for (Object obj : rule.getBody()) {
			if (obj instanceof Expression) {
				IATLExpression exp = new ATLExpressionAdapter((Expression) obj,
						rule, ExpressionPosition.rule);
				imperativeExps.add(exp);
			}
		}

		for (Object obj : rule.getVariables()) {
			if (obj instanceof Variable) {
				variables.add(new ATLVariableAdapter((Variable)obj));
			}
		}
	}

	@Override
	public List<IATLExpression> getImperativeExps() {
		ArrayList<IATLExpression> exps = new ArrayList<IATLExpression>();
		for (Object obj : rule.getBody()) {
			if (obj instanceof Expression)
				exps.add(new ATLExpressionAdapter((Expression) obj, rule,
						ExpressionPosition.rule));
		}
		return exps;
	}

	@Override
	public IATLPattern getSourcePattern() {
		return source;
	}

	@Override
	public List<IATLPattern> getTargetPattern() {
		ArrayList<IATLPattern> list = new ArrayList<IATLPattern>();
		list.add(target);
		return list;
	}

	@Override
	public EATLRuleType getType() {
		if (rule.getPriority() == 1) {
			return EATLRuleType.match;
		} else {
			return EATLRuleType.lazy;
		}
	}

	@Override
	public List<IATLVariable> getVariable() {
		return variables;
	}

	@Override
	public String getName() {
		return rule.getName();
	}

}
